package cn.tedu.charging.order.rabbit;

import cn.tedu.charging.order.config.RabbitConfiguration;
import cn.tedu.charging.order.constant.OrderStatusConstant;
import cn.tedu.charging.order.dao.mapper.OrderSuccessMapper;
import cn.tedu.charging.order.pojo.dto.ChargingDTO;
import cn.tedu.charging.order.pojo.dto.OrderMQDTO;
import cn.tedu.charging.order.pojo.po.ChargingBillSuccessPO;
import com.alibaba.nacos.shaded.org.checkerframework.checker.units.qual.A;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;

@Slf4j
@Component
public class RabbitMQOrderConsumer {
    @Autowired
    private OrderSuccessMapper orderSuccessMapper;

    // 把 queues 换成 DEAD_LETTER_QUEUE_NAME 相当于只有死信队列中有消费者了，而非死信队列中就没有消费者了
    @RabbitListener(queues = RabbitConfiguration.DEAD_LETTER_QUEUE_NAME)   // 这是Spring AMQP提供的监听器注解  表示该方法是一个Rabbimq消息消费者 参数指定要监听的队列名称
    /**
     * OrderMQDTO orderMQDTO：消息体内容，会自动反序列化为Java对象
     * Message message：原始消息对象，包含消息头、消息体等信息
     * Channel channel：RabbitMQ通道，用于确认消息处理结果
     *
     * 消息处理流程：
     *  当有消息到达指定队列时，Spring会自动调用这个方法
     *  消息会被自动反序列化为 OrderMQDTO 对象
     *  处理完成后需要手动确认消息处理成功
     */
    public void consumeChargingQueue(OrderMQDTO orderMQDTO, Message  message, Channel channel) throws IOException {
        log.debug("消费死信队列消息：{}", orderMQDTO);

        // 把状态是 正在充电中的 成功订单数据的状态 修改为 异常结束
        // update charging_bill_success
        // set bill_status = 3 (异常结束)
        // where bill_id = orderNo and bill_status = 1 (充电中)

        /**
         * 下面这种方法使用的是 mybatis 的方法，需要在 mappers 中写sql方法
         */
        ChargingBillSuccessPO chargingBillSuccessPO = new ChargingBillSuccessPO();
        // bill_status = 3
        chargingBillSuccessPO.setBillStatus(OrderStatusConstant.ORDER_STATUS_EXCEPTION_END);
        chargingBillSuccessPO.setBillId(orderMQDTO.getOrderNo());
        log.debug("将订单 orderNo {} 状态更新为异常结束", orderMQDTO.getOrderNo());
        orderSuccessMapper.updateOrderStatusFromProcessException(chargingBillSuccessPO);

        /**
         * 下面这种方法是 mybatis-plus 的方法，不需要在 mappers 中写 sql 方法
         */
        /*ChargingBillSuccessPO chargingBillSuccessPO = new ChargingBillSuccessPO();
        //bill_status = 3
        chargingBillSuccessPO.setBillStatus(OrderStatusConstant.ORDER_STATUS_EXCEPTION_END);

        //UpdateWrapper 相当于更新的条件 where bill_id = orderNo and bill_status = 1 (充电中)
        UpdateWrapper updateWrapper  = new UpdateWrapper();

        //bill_id = orderNo
        updateWrapper.eq("bill_id",orderMQDTO.getOrderNo());
        log.debug("将订单 {} 状态更新为异常结束", orderMQDTO.getOrderNo());
        //bill_status = 1
        updateWrapper.eq("bill_status", OrderStatusConstant.ORDER_STATUS_PROGRESS);
        orderSuccessMapper.update(chargingBillSuccessPO,updateWrapper);*/


        // 消息队列中的ack
        // consumeChargingQueue 消费者 从RabbitMQ 消费消息 ， 处理业务 更新订单状态
        // 正常情况  自动ack  默认
        //      更新订单状态,返回ack给消息队列
        //      消息队列收到消费者发送的 ack 后，任何消费者消费消息成功了，消息队列会把这条消息从队列中删除，消费者可以继续消费下一条消息
        // 异常情况
        //      更新订单状态，数据库出现异常，更新失败，消费者会抛出异常，如果是自动ack，还是返回ack给消息队列，消息队列收到 ack，删除消息
        //   改为手动ack，如果更新失败，不返回ack给消息队列
        //      消息队列没有收到ack，认为消费者没有成功消费消息，不会删除，会一直保存
        //   等数据库恢复，消费者会继续重新消费这条消息，继续更新订单的状态，消息没丢

        // 手动ack（测试方式：可以手动把下面的两行代码注释掉，会发现，消息队列中的消息不会被删除，会一直保留）
        // 消费者需要手动确认消息处理完成，而不是自动确认
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        channel.basicAck(deliveryTag,true);
    }
}
